#include <sys/mman.h>
#include <errno.h>
#include <error.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

// IP数据的统计信息
typedef struct DATA_STAT 
{
	int count; // 总记录数
	int type;  //  没有使用，但是必须是 1 或者 2
	int status; // 没有使用，但必须是 1 
	int offset; // 相对于文件开头，数据开始的位置
	int length; // 总记录数
} DATA_STAT;

// IP数据库
typedef struct DATA_DB
{
	void * pointer;  // IP数据库的指针
	DATA_STAT stat; // IP数据的统计信息
} DATA_DB; 

// IP数据库实例
typedef struct DB
{
	void * pointer;
	int length;
	DATA_DB region_db;
	DATA_DB ip_db;
	DATA_DB region_cache_db;
	DATA_DB region_en_cache_db;
} DB;

// 文件数据格式为:
// 	<Head info><IP DATA><REGION DATA><REGION CACHE DATA><REGION EN CACHE DATA>
// Head info 格式为:
// 	<IP STAT><REGION STAT><REGION CACHE STAT><REGION EN CACHE DATA>
// IP STAT / REGION STAT / REGION CACHE STAT / REGION EN CACHE STAT
//  <DATA_STAT>
// IP DATA 格式为:
//  <IP_INFO * n>
// REGION DATA 格式为:
//  <REGION_INFO * n>
// REGION CACHE DATA 格式为:
//  <REGION_CACHE * n>
// REGION EN CACHE DATA 格式为:
//  <REGION_CACHE * n>
DB db;


// 是否正在初始化数据库
bool is_init_db = false;

bool get_instance() 
{
	// MMAP 文件句柄
	int fd; 
	// 看MMAP文件是否是OK的，如果索引或者数据有问题，则重新生成
	bool flag = false; 

	DATA_STAT * stat;
	errno = 0;
	
	// 如果已经初始化过，直接返回 true
	if(db.pointer != NULL)  
	{
		return true;
	} 
	// 如果正在初始化，则直接返回false.
	else if(is_init_db) 
	{
		DEBUGX("Initing the db...\n");
		return false;
	}

	is_init_db = true;
	// 开始初始化, 首先以只读方式打开数据库文件
	fd = open(db_file, O_RDONLY, 0666);

	// 如果文件打开，但是文件长度不足，则视为文件索引已经损坏，所以需要关闭文件，重新生成文件
	if(fd > 0)
	{
		// 获取文件长度
		db.length = lseek(fd, 0, SEEK_END);
		if(db.length < sizeof(DATA_STAT) * 3)
		{
			close(fd);
			fd = -1;	
		}
	}
	else if(fd < 0 && errno != ENOENT)
	{
		DEBUGX3("Open file error: ", strerror(errno), "\n");
		is_init_db = false;
		return false;
	}

	// 如果文件描述符为负数，则需要初始化文件
	if(fd < 0)
	{
		if(!init_db())
		{
			is_init_db = false;
			return false;
		}
		fd = open(db_file, O_RDONLY, 0666);
	}

	if( fd > 0) 
	{
		// 获取文件长度
		db.length = lseek(fd, 0, SEEK_END);
		// 如果文件长度大于 DATA_STAT * 3，表明文件中有基本的IP/Region/Region_cache统计信息
		if(db.length > sizeof(DATA_STAT) * 3)
		{
			// 将文件全部 MMAP 到内存中
			db.pointer = mmap(NULL, db.length, PROT_READ, MAP_SHARED, fd, 0);

			if(db.pointer == MAP_FAILED)
			{
				DEBUGX3("Mmap failed, make sure the memmory is enough:", strerror(errno), "\n");
				close(fd);
				is_init_db = false;
				return false;
			}

			// 读取 IP 统计信息
			stat = (DATA_STAT *) memcpy(&db.ip_db.stat, (char *)db.pointer, sizeof(DATA_STAT));
			if(stat != NULL)
			{
				db.ip_db.pointer = (char *)db.pointer + stat->offset;
			}
			// 读取 Region 统计信息
			stat = (DATA_STAT *) memcpy(&db.region_db.stat, (char *)db.pointer + sizeof(DATA_STAT), sizeof(DATA_STAT));
			if(stat != NULL)
			{
				db.region_db.pointer = (char *)db.pointer + stat->offset;
			}
			// 读取Region Cache 统计信息
			stat = (DATA_STAT *) memcpy(&db.region_cache_db.stat,(char*) db.pointer + sizeof(DATA_STAT) * 2, sizeof(DATA_STAT));
			if(stat != NULL)
			{
				db.region_cache_db.pointer = (char *)db.pointer + stat->offset;
			}

			// 读取Region En Cache 统计信息
			stat = (DATA_STAT *) memcpy(&db.region_en_cache_db.stat,(char*) db.pointer + sizeof(DATA_STAT) * 3, sizeof(DATA_STAT));
			if(stat != NULL)
			{
				db.region_en_cache_db.pointer = (char *)db.pointer + stat->offset;
			}

			close(fd);
			is_init_db = false;

			return true;
		};
		close(fd);
	}
	DEBUGX3("Open file error", strerror(errno), "\n");
	is_init_db = false;
	return false;
}

void release_instance()
{
	if(db.pointer != NULL) 
	{
		munmap(db.pointer, db.length);
	}
}

bool get_ip_info(string ip, REGION_INFO * region_info) 
{
	uint uintip ;
	IP_INFO ip_info;
	if(is_init_db ) 
	{
		return false;
	}
	if(db.pointer == NULL) 
	{
		if(!get_instance()) 
		{
			return false;
		}
	}

	uintip  = htonl(inet_addr(ip.c_str()));

	if(bin_search_ip(uintip, & ip_info)) 
	{
		if(get_region_info(ip_info.region_id, region_info))
		{
			return true;
		}
	}
	return false;
}
bool get_region_info(int region_id, REGION_INFO * region_info)
{
	bool flag = false;
	DATA_DB * region_db = & db.region_db;

	if(region_id == 100000)
	{
		region_info->id = 100000;
		strncpy(region_info->name, "国外", MAX_NAME_LENGTH);
		region_info->level = 0;
		region_info->parent_id = -1;
		return true;
	}
	else if(region_id < region_db->stat.length && region_id >= 0)
	{
		memcpy(region_info, (char *) region_db->pointer + region_id * sizeof(REGION_INFO), sizeof(REGION_INFO));

		if(region_info->name != NULL)
		{
			return true;
		}
	}

	return false;
}

// 0 => 42301
// 1 => {minip = 0, maxip = 2, prov = 'beijing', city='beijing',code='1'};
bool bin_search_ip(uint ip, IP_INFO * ip_info) {
	bool found = false;
	bool ret = false;
	DATA_DB * ip_db = & db.ip_db;
	int low = 0, high = ip_db->stat.count, mid = 0;
	if( high <= 0) 
	{
		return found;		
	}

	while(low <= high) 
	{
		mid = (low + high) / 2 ;

		memcpy((char *) ip_info, ((char *) ip_db->pointer) + sizeof(IP_INFO) * mid, sizeof(IP_INFO));
		if(ip_info->minip <= ip && ip_info->maxip >= ip) 
		{
			found = true;
			break;
		} 
		else if(ip_info->minip < ip) 
		{
			low = mid + 1;
		} 
		else 
		{
			high = mid - 1;
		}
	}
	return found;
}


bool check_index() 
{
	return true;
}


int get_stat() 
{
	if(!get_instance())
 	{
		return 0;
	}

	return db.ip_db.stat.count;
}

static inline int compare_region_info(const void * a, const void * b)
{
	return strncmp(((REGION_INFO *) a)->name,((REGION_INFO *) b)->name, MAX_NAME_LENGTH);
}


static inline int compare_region_info_en(const void * a, const void * b)
{
	return strncmp(((REGION_INFO *) a)->en_name,((REGION_INFO *) b)->en_name, MAX_NAME_LENGTH);
}

static inline void free_region_info(REGION_INFO ** region_info, int len) {
	if(region_info != NULL && len > 0) {
		for( int i = 0 ; i < len; i ++) {
			if(region_info[i] != NULL) {
				free(region_info[i]);
				region_info[i] = NULL;
			}
		}
	}
	if(region_info != NULL) {
		free(region_info);
		region_info = NULL;
	}
}
inline static void free_region_info_x(REGION_INFO_X * region_info_x)
{
	int i, len;
	if(region_info_x != NULL)
	{
		if(region_info_x->length > 0 && region_info_x->childs != NULL)
		{
			for(i = 0, len = region_info_x->length; i < len; i ++)
			{
				if(region_info_x->childs[i] != NULL) {
					free_region_info_x(region_info_x->childs[i]);
					region_info_x->childs[i] = NULL;
				}
			}
		}
	 	if(region_info_x->childs != NULL){
			free(region_info_x->childs);	
			region_info_x->childs = NULL;
		}
		
		free(region_info_x);
		region_info_x = NULL;
	}
}
bool init_db() 
{
	int fd = 0;
	int ret = 0;
	
	int pos ;
	char line[MAX_LINE_LENGTH];
	char *p, *pstr;
	FILE * fp = fopen(src_file, "r");
	DATA_STAT stat;

	IP_INFO ip_info;
	REGION_INFO region_info;
	REGION_CACHE region_cache;

	uint sindex = 0, index = 1, len = 0, count = 0, max_id = 0;

	if(fp == NULL) 
	{
		DEBUGX3("Init IP info:  Cann't open ", (char*) src_file, " to read\n");
		return false;
	}

	fd = open(db_file, O_TRUNC|O_CREAT|O_RDWR, 0666);
	if(fd == -1)
	{
		DEBUGX3("Init DB: Open db file error: ", strerror(errno), "\n");	
		fclose(fp);
		return false;
	}

	// 开始初始化文件
	DEBUGX("Init DB: Begin to read data and init db!\n");
	
	// 开始初始化 IP 数据部分
	stat.count = 0;
	stat.type = 1;
	stat.status = 0;
	stat.offset = sizeof(DATA_STAT) * 4;
	stat.length = 0;

	memset(line, 0, MAX_LINE_LENGTH);
	lseek(fd, stat.offset, SEEK_SET);
	while((fgets(line, MAX_LINE_LENGTH - 1, fp)) != NULL)
	{
		if(line[0] == '\n' || line[0] == '#' || line[0] == '[') 
		{
			continue;
		}

		memset(& ip_info, 0x0, sizeof(IP_INFO));
		for( pstr = line, sindex = 0 ;
			 	((p = strsep(&pstr, " ")) != NULL) && sindex < 6; sindex ++ ) 
		{
			switch(sindex)
		 	{
				case 0:
					ip_info.minip = strtoll((char *) p, NULL, 10);	
					break;
				case 1:
					ip_info.maxip = strtoll((char *) p, NULL, 10);
					break;
				case 2:
					ip_info.region_id = atoi(p);
					break;
				default:
					;
			}	
		}
		if(write(fd, (void *) &ip_info, sizeof(IP_INFO)) > 0) 
		{
			count ++;
		}
	}
	fclose(fp);

	stat.count = count;
	stat.type = 1;
	stat.status = 1;
	stat.length = count;

	// write ip data stat to file.
	len = pwrite(fd, (void *) &stat, sizeof(DATA_STAT), 0);
	if(pos  == -1)
	{
		DEBUGX3("Write stat to db error: ", strerror(errno), "\n");
		close(fd);
		return false;
	}

	DEBUGX("Write ip data to db done!\n");

	// 开始初始化Region数据部分
	fp = fopen(region_file, "r");
	if(fp == NULL)
	{
		DEBUGX5("Open file ", (char *) region_file, " error: ", strerror(errno), "\n");
		close(fd);
		return false;
	}

	stat.offset = sizeof(DATA_STAT) * 4 + sizeof(IP_INFO) * stat.count;
	stat.count = 0;
	stat.type = 1;
	stat.status = 1;
	stat.length = 0;

	count = 0;
	lseek(fd, stat.offset, SEEK_SET);
	memset(line, 0, MAX_LINE_LENGTH);
	while((fgets(line, MAX_LINE_LENGTH - 1, fp)) != NULL)
	{
		if(line[0] == '\n' || line[0] == '#' || line[0] == '[') 
		{
			continue;
		}

		memset(& ip_info, 0x0, sizeof(IP_INFO));
		for( pstr = line, sindex = 0 ;
			 	((p = strsep(&pstr, ",")) != NULL) && sindex < 6; sindex ++ ) 
		{
			switch(sindex)
		 	{
				case 0:
					region_info.id = atoi(p);	
					break;
				case 1:
					len = strlen(p);
					memcpy(region_info.name, p, len);
					region_info.name[len] = '\0';
					break;
				case 2:
					region_info.parent_id = atoi(p);
					break;
				case 3:
					region_info.level = atoi(p);
					break;
				case 4:
					if(strcmp(p, "null") != 0) {
						len = strlen(p);
						memcpy(region_info.en_name, p, len);
						region_info.en_name[len] = '\0';
					} else {
						memcpy(region_info.en_name,"\0",1); 
					}
					break;
				case 5:
					region_info.center_id = atoi(p);
					break;
				default:
					;
			}	
		}
		if(pwrite(fd, (void *) &region_info, sizeof(REGION_INFO), stat.offset + sizeof(REGION_INFO) * region_info.id) > 0) 
		{
			count ++;
			if(region_info.id > max_id)
			{
				max_id = region_info.id;
			}
		}
	}
	fclose(fp);

	stat.count = count;
	stat.status = 1;
	stat.length = max_id + 1; // 因为要包含 0,所以 +1

	len = pwrite(fd, (void *) & stat, sizeof(DATA_STAT), sizeof(DATA_STAT));
	if(pos  == -1)
	{
		DEBUGX3("Write stat to db error: ", strerror(errno), "\n");
		close(fd);
		return false;
	}

	DEBUGX("Write region data to db done!\n");

	// 开始初始化 Region Cache Data 部分
	// 首先从文件中读取Region Data数据，然后按照给定的函数排序，最后写入到文件中

	p = (char *) malloc(stat.length * sizeof(REGION_INFO));
	if(p == NULL)
	{
		DEBUGX3("Allocate memory failed: ", strerror(errno), "\n");
		close(fd);
		return false;
	}

	pos = pread(fd, (void *) p, stat.length * sizeof(REGION_INFO), stat.offset);
	if(pos != stat.length * sizeof(REGION_INFO))
	{
		DEBUGX3("Read data from file error: ", strerror(errno), "\n");
		close(fd);
		return false;
	}

	qsort(p, stat.length,  sizeof(REGION_INFO), compare_region_info);

	len = stat.length;
	stat.offset = stat.offset + stat.length * sizeof(REGION_INFO);
	count = 0;

	lseek(fd, stat.offset, SEEK_SET);

	for(index = 0 ; index < len; index ++)
	{
		memcpy(& region_info, p + index * sizeof(REGION_INFO), sizeof(REGION_INFO));
		if(strcmp(region_info.name,"") != 0)
		{
			memcpy(region_cache.name, region_info.name, MAX_NAME_LENGTH);
			region_cache.id = region_info.id;
			if(write(fd, & region_cache, sizeof(REGION_CACHE)) > 0) {
				count ++;
			}
		}
	}

	stat.count = count;
	stat.status = 1;
	stat.length = count;
	stat.type = 1;
	len = pwrite(fd, (void *) & stat, sizeof(DATA_STAT), sizeof(DATA_STAT) * 2);
	if(len  == -1)
	{
		DEBUGX3("Write region cache stat to db error: ", strerror(errno), "\n");
		close(fd);
		return false;
	}

	DEBUGX("Write region data cache to db done!\n");


	// 开始初始化 Region En name Cache Data 部分
	// 首先从文件中读取Region Data数据，然后按照给定的函数排序，最后写入到文件中

	qsort(p, max_id + 1,  sizeof(REGION_INFO), compare_region_info_en);

	stat.offset = stat.offset + stat.length * sizeof(REGION_CACHE);
	count = 0;

	lseek(fd, stat.offset, SEEK_SET);
	for(index = 0, len = max_id + 1 ; index < len; index ++)
	{
		memcpy(& region_info, p + index * sizeof(REGION_INFO), sizeof(REGION_INFO));
		if(strcmp(region_info.en_name,"") != 0)
		{
			memcpy(region_cache.name, region_info.en_name, MAX_NAME_LENGTH);
			region_cache.id = region_info.id;
			if(write(fd, & region_cache, sizeof(REGION_CACHE)) > 0) {
				count ++;
			}
		}
	}

	stat.count = count;
	stat.status = 1;
	stat.length = count;
	stat.type = 1;
	len = pwrite(fd, (void *) & stat, sizeof(DATA_STAT), sizeof(DATA_STAT) * 3);
	if(len  == -1)
	{
		DEBUGX3("Write region en cache stat to db error: ", strerror(errno), "\n");
		close(fd);
		return false;
	}

	DEBUGX("Write region en data cache to db done!\n");

	close(fd);
	return true;
}

bool get_region_info_by_en_name(string en_name, REGION_INFO * region_info)
{
  bool found = false, flag = false;
  int ret = 0;
  DATA_DB * region_cache_db = & db.region_en_cache_db;
  int low = 0, mid = 0, high = region_cache_db->stat.count;
	char region_name[MAX_NAME_LENGTH];

  if( high <= 0)
  {
    return found;
  }

	REGION_CACHE region_cache;

	strncpy(region_name, en_name.c_str(), MAX_NAME_LENGTH);
  while(low <= high)
  {
    mid = (low + high) / 2 ;
    memcpy((char *) &region_cache, ((char *) region_cache_db->pointer) + sizeof(REGION_CACHE) * mid, sizeof(REGION_CACHE));
		ret = strcmp(region_cache.name, region_name);
		if(ret == 0 )
		{
			found = true;
			break;
		}
    else if(ret < 0)
    {
      low = mid + 1;
    }
    else
    {
      high = mid - 1;
    }
  }
	
	if(found)
	{
		//cerr << "id is " << region_cache.id << endl;
		return get_region_info(region_cache.id, region_info);
	}
  return false;
}
bool get_region_info_by_name(string name, REGION_INFO * region_info)
{
  bool found = false, flag = false;
  int ret = 0;
  DATA_DB * region_cache_db = & db.region_cache_db;
  int low = 0, mid = 0, high = region_cache_db->stat.count;
	char region_name[MAX_NAME_LENGTH];

  if( high <= 0)
  {
    return found;
  }

	REGION_CACHE region_cache;

	strncpy(region_name, name.c_str(), MAX_NAME_LENGTH);
  while(low <= high)
  {
    mid = (low + high) / 2 ;
    memcpy((char *) &region_cache, ((char *) region_cache_db->pointer) + sizeof(REGION_CACHE) * mid, sizeof(REGION_CACHE));
		ret = strcmp(region_cache.name, region_name);
		if(ret == 0 )
		{
			found = true;
			break;
		}
    else if(ret < 0)
    {
      low = mid + 1;
    }
    else
    {
      high = mid - 1;
    }
  }
	
	if(found)
	{
		//cerr << "id is " << region_cache.id << endl;
		return get_region_info(region_cache.id, region_info);
	}
  return false;
}

REGION_INFO ** get_region_parents(int region_id, int & len)
{
	int last = 0;
	REGION_INFO region_info;
	REGION_INFO ** region_parents;
	bool ret = get_region_info(region_id, &region_info);
	if(!ret)
	{
		return NULL;
	}

	len = 0;
	// 这儿目前最多是5个，所以申请 5 个
	region_parents = (REGION_INFO **)malloc(sizeof(REGION_INFO *) * 5);

	// 首先将自己保存在返回数组中
	while(ret && region_info.level >= 0)
	{
		region_parents[len] = (REGION_INFO *) malloc(sizeof(REGION_INFO));
		memcpy(region_parents[len], &region_info, sizeof(REGION_INFO));
		len ++;
		ret = get_region_info(region_info.parent_id, & region_info);
	}
	return region_parents;
}

REGION_INFO_X ** _get_region_childs(int region_id, int &len, int depth = 1)
{
	int i = 0, count = 0, last = 0;
	REGION_INFO region_info;
	REGION_INFO_X ** childs = NULL;
	DATA_DB * region_db = & db.region_db;
	
	//遍历所有可能的节点，然后获取需要的节点
	//根据 子节点的ID比父节点的ID大的规律，直接从父节点的ID + 1开始查找
	//
	
	len = 0;
	for( i = 0, count = region_db->stat.count; i < count ; i ++)
	{
		memcpy(&region_info, (char *)region_db->pointer + i * sizeof(REGION_INFO), sizeof(REGION_INFO));
		if(region_info.parent_id == region_id 
				&& region_info.id <= 100000
				&& strcmp(region_info.name, "") != 0)
		{
			if(len >= last)
			{
				last += 10;
				childs = len == 0 ? (REGION_INFO_X **) malloc(sizeof(REGION_INFO_X *) * last) : (REGION_INFO_X **) realloc(childs, sizeof(REGION_INFO_X *) * last);
			}
			childs[len] = (REGION_INFO_X *) malloc(sizeof(REGION_INFO_X));
			memcpy(&childs[len]->info, & region_info, sizeof(REGION_INFO));
			childs[len]->childs = depth - 1 > 0 ? _get_region_childs(region_info.id, childs[len]->length, depth - 1) : NULL;
			len ++;
		}
	}
	return childs;
}

REGION_INFO_X * get_region_childs(int region_id, int depth = 1)
{
	int i = 0, count = 0, last = 0;
	int len = 0; 
	REGION_INFO region_info;
	REGION_INFO_X * region_info_x = NULL;
	DATA_DB * region_db = & db.region_db;
	// 首先获取region 信息，如果没有获取到，则直接返回 NULL
	bool ret = get_region_info(region_id, &region_info);
	if(!ret) 
	{
		return NULL;
	}

	//为返回值申请内存
	region_info_x = (REGION_INFO_X *) malloc(sizeof(REGION_INFO_X));

	region_info_x->length = 0;
	//首先将自己保存在返回的数组中
	memcpy(&region_info_x->info, &region_info, sizeof(REGION_INFO));
	region_info_x->childs = _get_region_childs(region_id, region_info_x->length, depth);
	return region_info_x;

}

REGION_INFO ** get_all_regions(int &len)
{
	int count = db.region_db.stat.count;
	int length = db.region_db.stat.length;
	int j = 0;
	REGION_INFO region_info;
	REGION_INFO ** region_infos = (REGION_INFO **) malloc(sizeof(REGION_INFO *) * count);
	DATA_DB * region_db = & db.region_db;
	
	for(int i = 0; i < length; i ++)
	{
		memcpy(& region_info, (char *) region_db->pointer + sizeof(REGION_INFO) * i, sizeof(REGION_INFO));
		if(strcmp(region_info.name, "") != 0)
		{
			region_infos[j] = (REGION_INFO *) malloc(sizeof(REGION_INFO));
			memcpy(region_infos[j++], & region_info, sizeof(REGION_INFO));
		}
	}
	len = count;
	return region_infos;
}

REGION_INFO ** get_region_info_by_name_ex(string name, int &len)
{
  int ret = 0, length = 0, clen = 0;
  int low = 0, mid = 0, high = 0; 
	int last = 0, idx = 0, max = 0;
  DATA_DB * region_cache_db = & db.region_cache_db;
	char region_name[MAX_NAME_LENGTH];

	max = high = region_cache_db->stat.count;
  if( high <= 0) {
    return NULL;
  }

	REGION_CACHE region_cache;
	// first alloc 10 size.
	last = 10;
	REGION_INFO ** region_infos = (REGION_INFO **) malloc(sizeof(REGION_INFO **) * last);

	strncpy(region_name, name.c_str(), MAX_NAME_LENGTH);
	length = name.length();
  while(low <= high) {
    mid = (low + high) / 2 ;
    memcpy((char *) &region_cache, ((char *) region_cache_db->pointer) + sizeof(REGION_CACHE) * mid, sizeof(REGION_CACHE));
		clen = strlen(region_cache.name);
		ret = strncmp(region_cache.name, region_name, length > clen ? clen : length);
		if(ret == 0 ) {
			idx ++;
			break;
		} else if(ret < 0) {
      low = mid + 1;
    } else {
      high = mid - 1;
    }
  }
	if(idx < 1) {
		free(region_infos);
		return NULL;
	}
	region_infos[idx - 1] = (REGION_INFO *) malloc(sizeof(REGION_INFO));
	if(!get_region_info(region_cache.id, region_infos[idx -1])) {
		DEBUGX("Get region info error, may be you should regenerate the cache");
		free(region_infos);
		return NULL;
	}
	low = 1;
	high = mid - low;	
	while(high > 0) {
		memcpy(&region_cache, (char *)region_cache_db->pointer + sizeof(REGION_CACHE) * high, sizeof(REGION_CACHE));
		clen = strlen(region_cache.name);
		ret = strncmp(region_cache.name, region_name, length > clen ? clen : length);
		if(ret == 0) {
			if(idx >= last) {
				last += 10;
				region_infos = (REGION_INFO **) realloc(region_infos, sizeof(REGION_INFO **) * last);
			}	
			region_infos[idx] = (REGION_INFO *) malloc(sizeof(REGION_INFO));
			if(get_region_info(region_cache.id, region_infos[idx])) {
				idx ++;
			} else {
				break;
			}
		}
		low ++;
		high = mid - low;	
	}

	low = 1;
	high = mid + low;	
	while(high < max) {
		memcpy(&region_cache, (char *)region_cache_db->pointer + sizeof(REGION_CACHE) * high, sizeof(REGION_CACHE));
		clen = strlen(region_cache.name);
		ret = strncmp(region_cache.name, region_name, length > clen ? clen : length);
		if(ret == 0) {
			if(idx >= last) {
				last += 10;
				region_infos = (REGION_INFO **) realloc(region_infos, sizeof(REGION_INFO **) * last);
			}	
			region_infos[idx] = (REGION_INFO *) malloc(sizeof(REGION_INFO));
			if(get_region_info(region_cache.id, region_infos[idx])) {
				idx ++;
			} else {
				break;
			}
		}
		low ++;
		high = mid + low;
	}

	if(idx > 0) {
		len = idx;
		return region_infos;
	}
	free(region_infos);
	return NULL;
}
